/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiInstallScript.cpp
	Functions that execute install scripts, and interfaces for declaring
	functions that can be used by insall scripts.

******************************************************************************/

#include "MaxsiEngine.h"

BeginMaxsiNamespace

List MISPermissions		=	NULL;

MESTR* MaxsiInstallScriptInput::operator[]( size_t val )
{
	return (MESTR*)GetListEntry(Var, val);
}

void MaxsiInstallScriptInput::operator =(List Input)
{
	Var		=	Input;
	Size	=	GetListSize(Input);
}

List MaxsiInstallScriptInput::operator =(MaxsiInstallScriptInput Input)
{
	return Var;
}

MaxsiInstallScriptInput::MaxsiInstallScriptInput(List Input)
{
	Var		=	Input;
	Size	=	GetListSize(Input);
}


MaxsiInstallScriptInput::MaxsiInstallScriptInput()
{
	Var		=	0;
	Size	=	0;
}

// Register a function that can be called from MIS Scripts
bool MISFuncRegister(MESTR* Name, MISFunc Pointer, MISPermission Permission)
{
	bool	Result	=	false;

	// Set up basic defines
	MISFuncS		S;
	MISFuncP		P;
	S.Name		=	Name;
	S.Pointer	=	Pointer;

	if ( !MISPermissions )
	{
		// Do these things here, because it's faster if we don't have a MISPermissions list.
		MISPermissions	=	CreateList(1);

		// Check for error conditions.
		if ( MISPermissions == NULL ) { return false; }

		P.Functions		=	CreateList(1);

		if ( P.Functions == NULL ) { DeleteList(MISPermissions); MISPermissions = NULL; return false; }
		
		P.Name			=	Permission;

		if ( SetListEntry(P.Functions,	0,(BYTE*)&S,sizeof(S)) == false ) { DeleteList(MISPermissions); MISPermissions = NULL; DeleteList(P.Functions); return false; }

		if ( SetListEntry(MISPermissions,0,(BYTE*)&P,sizeof(P)) == false ) { DeleteList(MISPermissions); MISPermissions = NULL; DeleteList(P.Functions); return false; }

		Result			= true;
	}
	else
	{
		// First find the proper premission
		size_t	nMISPermissions		=	GetListSize(MISPermissions);

		for (size_t N = 0; N < nMISPermissions; N++)
		{
			memcpy(&P,GetListEntry(MISPermissions,N),sizeof(P));

			if ( MESTRICMP(Permission, P.Name) == 0)
			{
				// Then see if the function already exists.
				bool	AlreadyRegistered	=	false;
				size_t	nFunctions			=	GetListSize(P.Functions);

				for (size_t I = 0; I < nFunctions; I++)
				{
					memcpy(&S,GetListEntry(P.Functions,I),sizeof(P));

					if ( MESTRICMP(Name, S.Name) == 0)
					{
						// The function is already registered,
						// don't add it twice.
						AlreadyRegistered	=	true;
					}
				}

				if ( !AlreadyRegistered )
				{				
					// Reset the variables
					S.Name		=	Name;
					S.Pointer	=	Pointer;
					
					AddListEntry(&P.Functions, (BYTE*)&S, sizeof(S));
		
					MAXSI_POSSIBLE_ALLOC_FAILURE();
					
					Result		=	true;
				}
				else
				{
					Result		=	true;
				}
			}

			// The correct permission was not found, create a new one!
			if ( Result == false )
			{					
				P.Functions		=	CreateList(1);
				MAXSI_POSSIBLE_ALLOC_FAILURE();
				P.Name			=	Permission;

				// Simply fill it into the table, expand as needed, and put the first function in as well!
				SetListEntry(P.Functions,	0,(BYTE*)&S,sizeof(S));
				MAXSI_POSSIBLE_ALLOC_FAILURE();
				SetListEntry(MISPermissions,0,(BYTE*)&P,sizeof(P));
				MAXSI_POSSIBLE_ALLOC_FAILURE();
				AddListEntry(&MISPermissions, (BYTE*)&P, sizeof(P));
				MAXSI_POSSIBLE_ALLOC_FAILURE();

				Result			=	true;
			}
		}
	}
	
	return Result;
}

MISFunc MISFuncGet(MESTR* Name, MISPermission Permission)
{
	MISFunc	Result	=	0;

	// Set up basic defines
	MISFuncS		S;
	MISFuncP		P;

	if ( MISPermissions )
	{
		// First find the proper premission
		size_t	nMISPermissions		=	GetListSize(MISPermissions);

		for (size_t N = 0; N < nMISPermissions; N++)
		{
			memcpy(&P, GetListEntry(MISPermissions, N), sizeof(P));

			if ( MESTRICMP(Permission, P.Name) == 0)
			{
				// Then see if the function already exists.
				size_t	nFunctions	=	GetListSize(P.Functions);

				for (size_t M = 0; M < nFunctions; M++)
				{
					memcpy(&S, GetListEntry(P.Functions, M), sizeof(P));

					if ( MESTRICMP(Name, S.Name) == 0)
					{
						return S.Pointer;
					}
				}
			}
		}
	}
	
	return Result;
}

// Parses a Application/x-MaxsiEngineInstallerScript/2.0 script
// and executes it and returns the result.
int MISRun(MESTR* Script, MISPermission Permission)
{
	// This entire function is unprotected!
	MAXSI_POSSIBLE_ALLOC_FAILURE();

	// Variables
	size_t		Read		=	0;
	size_t		NextR		=	0;
	size_t		NextN		=	0;
	size_t		NextLine	=	0;
	size_t		NextLineLen	=	0;
	size_t		NextWS		=	0;
	size_t		LeadingWS	=	0;
	size_t		argc		=	0;
	size_t		ParamLen	=	0;
	MESTR*		Line		=	Script;
	MESTR*		Function	=	0;
	MESTR*		Param		=	0;
	bool		Headers		=	false;
	bool		HeadersDone	=	false;
	bool		bNoSupport	=	false;
	bool		LastLine	=	false;
	MISInput	In			=	0;
	List		InList		=	0;

	// Run through every line!
	while ( true )
	{
		// Simply point to the location of the script
		// pointing is father than copying

		Line				=	Script+Read;

		// Find the lenght of the line, no matter how the linebreaks are encoded.
		NextR		=	FindChar(Line, '\r', MESTRLEN(Line));
		NextN		=	FindChar(Line, '\n', MESTRLEN(Line));

		if ( NextR < NextN ) { NextLine = NextR; } else { NextLine = NextN; }

		if ( NextLine == SIZE_MAX )
		{
			// Hmm.
			LastLine	=	true;
			NextLine	=	MESTRLEN(Line);
		}

		if ( Script[Read+NextLine] == '\r' &&
			 Script[Read+NextLine+1] == '\n')
		{
			NextLineLen		=	2;				
		}
		else
		{
			NextLineLen		=	1;
		}

		LeadingWS			=	SkipWhiteSpace(Line, NextLine);

		// Check if there's any content in this line, and if so, skip it
		if ( LeadingWS == SIZE_MAX )
		{
			if ( Headers ) { HeadersDone = true; }
			// No content. Simply skip to next line.
			Read			+=	NextLine+NextLineLen;
			if ( LastLine )	{ break; }
			continue;
		}

		Line				=	Line+SkipWhiteSpace(Line, NextLine);

		// Find the function name.

		NextWS				=	NextNonQuotedWhiteSpace(Line, NextLine);

		if ( NextWS == SIZE_MAX )
		{
			// No arguments, just a function.
			Function				=	new MESTR[NextLine+1];
			Function[NextLine]	=	0;
			memcpy(Function, Line, NextLine);
			InList					=	CreateList(0);
			argc					=	0;
		}
		else
		{
			Function				=	new MESTR[NextWS+1];
			Function[NextWS]		=	0;
			memcpy(Function, Line, NextWS);

			// Count the number of arguments!
			argc					=	0;				
			NextWS					=	NextWS+SkipWhiteSpace(Line+NextWS+1, NextLine-NextWS);	

			while ( NextNonQuotedWhiteSpace(Line+NextWS, NextLine-NextWS) != SIZE_MAX )
			{
				argc++;
				if ( NextNonQuotedWhiteSpace(Line+NextWS+1 ,NextLine-NextWS) == SIZE_MAX ) { break; }
				NextWS	=	NextWS + NextNonQuotedWhiteSpace(Line+NextWS+1, NextLine-NextWS);
				if ( SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS) == SIZE_MAX ) { break; }
				NextWS	=	NextWS + SkipWhiteSpace(Line+NextWS+1, NextLine-NextWS);
			}

			InList					=	CreateList(argc);

			// Copy the arguments into the array
			
			// Reset variables
			NextWS					=	NextNonQuotedWhiteSpace(Line, NextLine);		
			NextWS					=	NextWS + SkipWhiteSpace(Line+NextWS+1, NextLine-NextWS);	
			argc					=	0;


			while ( NextNonQuotedWhiteSpace(Line+NextWS, NextLine-NextWS) != SIZE_MAX )
			{
				argc++;
				//ParamLen			=	min(NextNonQuotedWhiteSpace(Line+NextWS+1,NextLine-NextWS),strlen(Line));
				if ( NextNonQuotedWhiteSpace(Line + NextWS+1, NextLine-NextWS) < MESTRLEN(Line) )
				{
					ParamLen		=	NextNonQuotedWhiteSpace(Line+NextWS+1, NextLine-NextWS);
				}
				{
					ParamLen		=	MESTRLEN(Line);
				}

				Param				=	new MESTR[ParamLen+1];
				Param[ParamLen]		=	0;

				memcpy(Param,Line+NextWS+1,ParamLen);

				MESTR*		Variable	=	EvaluateQuote(Param);

				SetListEntryM(InList, argc-1, Variable);
				delete[]	Variable;

				if ( NextNonQuotedWhiteSpace(Line+NextWS+1,NextLine-NextWS) == SIZE_MAX ) { break; }
				NextWS = NextWS+NextNonQuotedWhiteSpace(Line+NextWS+1, NextLine-NextWS);
				if ( SkipWhiteSpace(Line+NextWS+1,NextLine-NextWS) == SIZE_MAX ) { break; }
				NextWS = NextWS+SkipWhiteSpace(Line+NextWS+1, NextLine-NextWS);
			}
		}

		if ( MESTRCMP(Function,"//") == 0 )
		{
			// Comment. IGNORE.
		}
		else
		{
			if ( !Headers && !HeadersDone )
			{
				Headers = true;
				if ( MESTRCMP(Function, _MESTR("Application/x-MaxsiEngineInstallerScript/2.0")) != 0 )
				{
					bNoSupport = true;
				}					
			}
			else if ( Headers && !HeadersDone )
			{
				// still boring header stuff. Skip!
				// TODO: Parse these important variables!
			}
			else
			{
				// If we're here, then we better execute something!
				MISFunc		CallMe		=	MISFuncGet(Function, Permission);

				if ( !CallMe )
				{
					// fail!
					bNoSupport = true;
				}
				else
				{
					In		=		InList;

					CallMe(In);
				}
			}
		}

		delete[] Function;
		DeleteList(InList);

		if ( bNoSupport )
		{
			return 0;
		}
		
		Read		=	Read+NextLine+NextLineLen;

		if ( LastLine )
		{
			break;
		}
	}

	return 1;
}

EndMaxsiNamespace

